package com.bsming.code;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

/**
 * @author magenm
 * @date 2018/7/17 9:57
 */
public class GeneratorCode {

    private String pkgName;
    private String modelName;
    private String modelPrefix;
    private String author;
    private String path;
    private String driverName = "com.mysql.jdbc.Driver";
    private String username;
    private String password;
    private String url;
    private String tbPrefix;

    public GeneratorCode(String pkgName, String modelName, String modelPrefix,
                         String dbUrl, String dbUserName, String dbPassword,
                         String tbPrefix, String author) {
        this.pkgName = pkgName;
        this.modelName = modelPrefix + modelName;
        this.modelPrefix = modelPrefix;
        this.username = dbUserName;
        this.password = dbPassword;
        this.url = dbUrl;
        this.tbPrefix = tbPrefix;
        this.author = author;
        File file = new File(this.modelName);
        this.path = file.getAbsolutePath();
    }

    public void generator() {
        AutoGenerator autoGen = new AutoGenerator();
        autoGen.setGlobalConfig(config());
        autoGen.setDataSource(datasource());
        autoGen.setStrategy(strategy());
        autoGen.setPackageInfo(pkgConfig());
        // service模块中生成*Mapper.xml
        if ((modelPrefix + "service").equals(modelName)) {
            autoGen.setCfg(xmlConfig());
        }
        autoGen.setTemplate(tplConfig());
        // 执行生成
        autoGen.execute();
        // 后续处理
        afterProcess();

    }

    /**
     * 后续处理
     */
    private void afterProcess() {
        if ((modelPrefix + "web").equals(modelName)) {
            genResources();
            genApplicationJava();
        }
    }

    /**
     * 生成资源文件
     */
    private void genResources() {
        try {
            File file = new File(path + "/src/main/resources/a");
            file.mkdirs();
            file.delete();

            Files.copy(Paths.get("src/main/resources/application.yml"),
                    new FileOutputStream(path + "/src/main/resources/application.yml"));
            Files.copy(Paths.get("src/main/resources/application-dev.yml"),
                    new FileOutputStream(path + "/src/main/resources/application-dev.yml"));
            Files.copy(Paths.get("src/main/resources/application-prod.yml"),
                    new FileOutputStream(path + "/src/main/resources/application-prod.yml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成Application
     */
    private void genApplicationJava() {
        try {
            List<String> dists = new ArrayList<>();
            List<String> lines = Files.readAllLines(Paths.get("src/main/resources/application.vm"));
            lines.forEach(line -> {
                if (line.contains("{pkgName}")) {
                    dists.add(line.replace("{pkgName}", pkgName));
                } else {
                    dists.add(line);
                }
            });

            String javaFile = path + "/src/main/java/" + pkgName.replace(".", "/")
                    + "/Application.java";
            FileOutputStream fos = new FileOutputStream(javaFile);
            PrintWriter pw = new PrintWriter(fos);
            dists.forEach(line -> {
                pw.write(line + "\n");
            });

            pw.flush();
            pw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 数据源配置
     *
     * @return
     */
    private DataSourceConfig datasource() {

        DataSourceConfig dsConf = new DataSourceConfig();
        dsConf.setDbType(DbType.MYSQL);
        dsConf.setTypeConvert(new MySqlTypeConvert() {
            // 自定义数据库表字段类型转换【可选】
            @Override
            public DbColumnType processTypeConvert(String fieldType) {
                System.out.println("转换类型：" + fieldType);
                // 注意！！processTypeConvert 存在默认类型转换，如果不是你要的效果请自定义返回、非如下直接返回。
                return super.processTypeConvert(fieldType);
            }
        });
        dsConf.setDriverName(driverName);
        dsConf.setUsername(username);
        dsConf.setPassword(password);
        dsConf.setUrl(url);
        return dsConf;
    }

    /**
     * 全局配置
     *
     * @return
     */
    private GlobalConfig config() {
        GlobalConfig config = new GlobalConfig();
        config.setOutputDir(path + "/src/main/java");
        config.setFileOverride(true);
        config.setActiveRecord(true);
        config.setEnableCache(false);
        config.setBaseResultMap(true);
        config.setBaseColumnList(false);
        config.setAuthor(author);

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        config.setMapperName("%sMapper");
        config.setXmlName("%sMapper");
        config.setServiceName("%sService");
        config.setServiceImplName("%sServiceImpl");
        config.setControllerName("%sController");
        return config;
    }

    /**
     * 策略配置
     *
     * @return
     */
    private StrategyConfig strategy() {

        StrategyConfig strategy = new StrategyConfig();
        strategy.setTablePrefix(new String[] {tbPrefix});
        // 表名生成策略
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 需要排除生成的表
        strategy.setExclude("test");
        // 自定义实体，公共字段
        strategy.setSuperEntityColumns(new String[] {"id"});
        return strategy;
    }

    /**
     * 包配置
     *
     * @return
     */
    private PackageConfig pkgConfig() {
        PackageConfig pkgConfig = new PackageConfig();
        pkgConfig.setParent(pkgName);
        pkgConfig.setController("controller");
        pkgConfig.setEntity("entity");
        pkgConfig.setMapper("mapper");
        pkgConfig.setService("service");
        pkgConfig.setServiceImpl("impl");
        return pkgConfig;
    }

    /**
     * 調整*Mapper.xml位置
     *
     * @return
     */
    private InjectionConfig xmlConfig() {
        InjectionConfig config = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                this.setMap(map);
            }
        };
        List<FileOutConfig> focList = new ArrayList<FileOutConfig>();
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return path + "/src/main/resources/mappers/" + tableInfo.getEntityName() + ".xml";
            }
        });
        config.setFileOutConfigList(focList);
        return config;
    }

    /**
     * 模板配置
     * 关闭默认 xml 生成
     * 调整生成至根目录
     *
     * @return
     */
    private TemplateConfig tplConfig() {

        TemplateConfig tplConfig = new TemplateConfig();
        tplConfig.setXml(null);
        if ((modelPrefix + "common").equals(modelName)) {
            tplConfig.setController(null);
            tplConfig.setServiceImpl(null);
        } else if ((modelPrefix + "mapper").equals(modelName)) {
            tplConfig.setController(null);
            tplConfig.setEntity(null);
            tplConfig.setService(null);
            tplConfig.setServiceImpl(null);
        } else if ((modelPrefix + "model").equals(modelName)) {
            tplConfig.setController(null);
            tplConfig.setService(null);
            tplConfig.setServiceImpl(null);
            tplConfig.setMapper(null);
        } else if ((modelPrefix + "service").equals(modelName)) {
            tplConfig.setController(null);
            tplConfig.setMapper(null);
            tplConfig.setEntity(null);
            tplConfig.setService(null);
        } else if ((modelPrefix + "web").equals(modelName)) {
            tplConfig.setMapper(null);
            tplConfig.setService(null);
            tplConfig.setServiceImpl(null);
            tplConfig.setEntity(null);
        }
        return tplConfig;
    }
}
